
#include <stdio.h>
#include <stdlib.h>
#include "network.h"
#include "tools.h"
#include "rand.h"

typedef struct {
	         double l_vals[ 3 ];
	       } LINK, *L_P;

typedef struct {
		 double u_vals[ 2 ];
		 L_P	links;
	       } UNIT, *U_P;

typedef struct {
	         double *in;		// pointer to inputs
		 double *out;		// pointer to the outputs
	       } PAT, *P_P;

typedef struct {
		 int sizes[ 3 ];	// number of units per layer incl. bias
		 int offset[ 4 ];	// start offset; sum_(i=0)^(u-1) size[i]
		 int layers;		// looks weired but makes some func.
		               		// more generic, ie, more hidden layers
					// offset[ 3 ] = tot_units
		 int pats;		// number of patterns
		 U_P units;		// pointer to the units
		 P_P patterns;
	       } NETWORK, *N_P;

static N_P np = 0;

static char *lnames[] = { "input", "hidden", "output" };

static int progerr( char *fnc, char *txt, int iarg, char *sarg, int quit )
       {
          fflush( stdout );
          fprintf( stderr,"%s: sorry, but you have made a fatal programming error\n",fnc);
	  if ( txt )
	  {
             fprintf( stderr, "\t" );
             fprintf( stderr, txt, iarg, sarg );
             fprintf( stderr, "\n" );
	  }
	  if ( quit )
	     exit( 1 );
       }

#define fprogerr( fnc, txt, iarg, sarg )	progerr( fnc, txt, iarg, sarg, 1 )

static int check_nn( char *fnc )
       {
	  if ( ! np )
	     fprogerr( fnc, "no network present :-(", 0, 0 );
       }

static int check_layer( char *fnc, int l )
       {
	  check_nn( fnc );
	  if ( l != IN && l != HID && l != OUT )
	     fprogerr( fnc, "no such layer %d", l, 0 );
       }

static int check_unit( char *fnc, int l, int u )
       {
	  check_layer( fnc, l );
	  if ( u < 0 || u >= np->sizes[ l ] )
	     fprogerr( fnc, "no such unit %d in %s", u, lnames[ l ] );
	  return np->offset[ l ] + u;
       }

static int check_pattern( char *fnc, int p )
       {
	  check_nn( fnc );
          if ( p < 0 || p >= np->pats )
	     fprogerr( fnc, "no such pattern %d", p, 0 );
          return p;
       }

int make_nn( int in, int hid, int out, int pat )
    {
       int i, j;
       if ( np )
          fprogerr( "make_nn", "you have already created a network; destroy it first", 0, 0 );
       if ( in < 1 )
          fprogerr( "make_nn", "you need at least in=1 input unit (rather than %d)", in, 0 );
       if ( hid < 1 )
          fprogerr( "make_nn", "you need at least hid=1 hidden unit (rather than %d)", hid, 0 );
       if ( out < 1 )
          fprogerr( "make_nn", "you need at least out=1 output unit (rather than %d)", out, 0 );
       if ( pat < 1 )
          fprogerr( "make_nn", "you need at least pat=1 patterns (rather than %d)", pat, 0 );
       np = (N_P) m_alloc( sizeof(NETWORK), "make_nn1" );
       np->sizes[ 0 ] = in;
       np->sizes[ 1 ] = hid;
       np->sizes[ 2 ] = out;
       np->layers = 3;
       for( np->offset[ 0 ] = 0, i = 1; i <= np->layers; i++ ) 
          np->offset[ i ] = np->sizes[i - 1] + np->offset[i - 1];
       // making the array of units
       np->units = (U_P) m_alloc( sizeof( UNIT ) * np->offset[ np->layers ],
       								  "make_nn2" );
       // setting the bias units
       for( i = 0; i < np->layers - 1; i++ )
       {
          np->units[np->offset[i + 1] - 1].u_vals[ ACT ] = 1.0;
          np->units[np->offset[i + 1] - 1].u_vals[ ERR ] = 0.0;
       }
       // allocating the connections
       for( j = 0; j < np->sizes[ 0 ]; j++ ) 
	  np->units[ j ].links = 0;		// inputs don't have weights
       for( i = 1; i < np->layers; i++ ) 	// for each layer
          for( j = np->offset[ i ]; j < np->offset[i + 1]; j++ ) 	// RS
             np->units[ j ].links = (L_P) m_alloc( sizeof( LINK )
	     				* np->offset[ i ], "make_nn3" );
       // making the patterns
       np->pats = pat;
       np->patterns = (P_P) m_alloc( sizeof( PAT ) * pat, "make_nn4" );
       for( i = 0; i < pat; i++ )
       {
          np->patterns[ i ].in  = (double *) m_alloc( sizeof(double) * in,
	  							"make_nn5" );
          np->patterns[ i ].out = (double *) m_alloc( sizeof(double) * out,
	  							"make_nn6" );
       }
       return 1;
    }

int destroy_nn()
    {
       int i;
       check_nn( "destroy_nn" );
       for( i = 0; i < np->pats; i++ )
       {
          m_free( (char *) np->patterns[ i ].in , "destroy_nn1" );
          m_free( (char *) np->patterns[ i ].out, "destroy_nn2" );
       }
       m_free( (char *) np->patterns, "destroy_nn3" );
       for( i = np->offset[ 1 ]; i < np->offset[ np->layers ]; i++ )
          m_free( (char *) np->units[ i ].links, "destroy_nn4" );
       m_free( (char *) np->units, "destroy_nn5" );
       m_free( (char *) np, "destroy_nn6" );
       np = 0;
    }

int init_units( double min_w, double max_w )
    {
       int i, j, k, l;
       double dif = max_w - min_w;
       check_nn( "init_units" );
       for( l = 1; l < np->layers; l++ )
	   for( i = np->offset[ l ]; i < np->offset[l + 1]; i++ ) // all units per layer
	     for( j = 0; j < np->offset[ l ]; j++ )	// for every input connection
	     {
		for( k = 0; k < sizeof( LINK )/sizeof( double ); k++ )
		   np->units[ i ].links[ j ].l_vals[ k ]  = 0;
		np->units[ i ].links[ j ].l_vals[ WEIGHT ] = min_w + dif * drand();
	     }
    }

double get_link( int type, int tl, int tu, int fl, int fu )
       {
	  if (tl <= fl) fprogerr( "get_link", "target layer must be higher than source layer", 0, 0 );  
          tu = check_unit( "get_link (to unit)"  , tl, tu );
          fu = check_unit( "get_link (from unit)", fl, fu );
	  if ( type < 0 || type > PDELTA )
	     fprogerr( "get_link", "illegal component '%d'", type, 0 );
	  return np->units[ tu ].links[ fu ].l_vals[ type ];
       }

int set_link( int type, int tl, int tu, int fl, int fu, double val )
    {
       if (tl <= fl) fprogerr( "set_link", "target layer must be higher than source layer", 0, 0 );  
       tu = check_unit( "set_link (to unit)"  , tl, tu );
       fu = check_unit( "set_link (from unit)", fl, fu );
       if ( type < 0 || type > PDELTA )
	  fprogerr( "set_link", "illegal component '%d'", type, 0 );
       np->units[ tu ].links[ fu ].l_vals[ type ] = val;
    }

double get_unit( int type, int l, int u )
       {

	  if ( type < 0 || type > ERR )
	     fprogerr( "get_unit", "illegal component '%d'", type, 0 );
          return np->units[ check_unit( "get_unit", l, u ) ].u_vals[ type ];
       }

int set_unit( int type, int l, int u, double val )
    {
       int i;
       if ( type < 0 || type > ERR )
	  fprogerr( "set_unit", "illegal component '%d'", type, 0 );
       i = check_unit( "set_act", l, u );
       if ( l != OUT )
	  if ( u == np->sizes[ l ]  - 1 )
	     fprogerr( "set_act", "sorry, you do not have write accessare to the bias unit (unit nr. %d) of the %s layer", u, lnames[ l ] );
       np->units[ i ].u_vals[ type ] = val;
    }

double get_pattern( int p, int l, int u )
       {
          check_pattern( "get_pattern", p );
	  if ( l == IN || l == OUT )
	     check_unit( "get_pattern", l, u );
	  else fprogerr( "get_pattern", "sorry, no pattern for layer %d", l, 0 );
	  // return np->patterns[ p ].in[ u ];
	  if ( l == IN )
	     return np->patterns[ p ].in[ u ];
	  else return np->patterns[ p ].out[ u ];
       }

int set_pattern( int p, int l, int u, double val )
    {
       check_pattern( "get_pattern", p );
       if ( l == IN || l == OUT )
	  check_unit( "set_pattern", l, u );
       else fprogerr( "set_pattern", "sorry, no pattern for layer %d", l, 0 );
       if ( l == IN )
	  np->patterns[ p ].in[ u ] = val;
       else np->patterns[ p ].out[ u ] = val;
    }

int set_cpattern( int p, double *iv_p, double *ov_p )
    {
       int i;
       check_pattern( "get_pattern", p );
       for( i = 0; i < np->sizes[ IN ] - 1; i++ )
	  np->patterns[ p ].in[ i ] = iv_p[ i ];
       for( i = 0; i < np->sizes[ OUT ]; i++ )
	  np->patterns[ p ].out[ i ] = ov_p[ i ];
    }




